package hellfire.system.graphics;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Vector;

import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;
import hellfire.system.engine.Main;

public class GLParticleEffect implements GLDrawable
{
	//Shader program ID
	private final int program;
    
    //Position & Size properties
    private GLVec2 pos, dim;
    
    //Cache for handles
    private GLCache cache = new GLCache();
    
    //Simulation time
    private float simTime = 0;

    //All the good buffers
    private FloatBuffer vertexCoordBuffer, vertexVelocityBuffer, vertexColorBuffer, vertexSizeBuffer;

    //Number of particles
    private final int numberOfParticles;
    
    public static class Particle
    {
    	public Particle(GLVec2 velocity, float size, GLColor color)
    	{
    		this.velocity = velocity;
    		this.size = size;
    		this.color = color;
    	}
    	
    	public GLVec2 velocity;
    	public float size;
    	public GLColor color;
    };
    
    public GLParticleEffect(GLVec2 pos, GLVec2 dim, Vector<Particle> particleData)
    {
    	numberOfParticles = particleData.size();
    	setupBuffers(particleData);
    	this.program = Main.UI.renderer.getShaderProgram(Shaders.pSpark);
    	//particleData.add(null);
    	this.pos = pos;
    	this.dim = dim;
    }

    public void setSimulationTime(float simTime)
    {
    	this.simTime = simTime;
    }
    
    public void updateBuffers(Vector<Particle> particleData)
    {
    	updateVelocities(particleData);
    	updateVertexCoords(particleData);
    	updateSizes(particleData);
    	updateColors(particleData);
    }
    
    public void updateVelocities(Vector<Particle> particleData)
    {
    	for(int i = 0; i < particleData.size(); i++) 
        {
        	Particle p = particleData.get(i);
        	vertexVelocityBuffer.put(p.velocity.x());
        	vertexVelocityBuffer.put(p.velocity.y());
       	}
        vertexVelocityBuffer.position(0);
    }
    
    public void updateVertexCoords(Vector<Particle> particleData)
    {
    	for(int i = 0; i < particleData.size()*3; i++) vertexCoordBuffer.put(0.0f);
        vertexCoordBuffer.position(0);
    }
    
    public void updateSizes(Vector<Particle> particleData)
    {
    	for(int i = 0; i < particleData.size(); i++) 
        {
        	Particle p = particleData.get(i);
        	vertexSizeBuffer.put(p.size);
       	}
        vertexSizeBuffer.position(0);
    }
    
    public void updateColors(Vector<Particle> particleData)
    {
    	for(int i = 0; i < particleData.size(); i++) 
        {
        	Particle p = particleData.get(i);
        	vertexColorBuffer.put(p.color.asFloatArray());
       	}
        vertexColorBuffer.position(0);
    }
    
    
    private void setupBuffers(Vector<Particle> particleData)
    {
    	ByteBuffer bb = ByteBuffer.allocateDirect(particleData.size() * 12); //vertex coord is (x,y,z), 3 floats => 12 bytes
        bb.order(ByteOrder.nativeOrder());
        vertexCoordBuffer = bb.asFloatBuffer();
        updateVertexCoords(particleData);
        
        bb = ByteBuffer.allocateDirect(particleData.size() * 8); //vertex vel is (x,y), 2 floats => 8 bytes
        bb.order(ByteOrder.nativeOrder());
        vertexVelocityBuffer = bb.asFloatBuffer();
        updateVelocities(particleData);
        
        bb = ByteBuffer.allocateDirect(particleData.size() * 16); //vertex color is (r,g,b,a), 4 floats => 16 bytes
        bb.order(ByteOrder.nativeOrder());
        vertexColorBuffer = bb.asFloatBuffer();
        updateColors(particleData);
        
        bb = ByteBuffer.allocateDirect(particleData.size() * 4); //vertex size is 1 float => 4 bytes
        bb.order(ByteOrder.nativeOrder());
        vertexSizeBuffer = bb.asFloatBuffer();
        updateSizes(particleData);
    }
    
	@Override
	public void draw(float[] matrix) 
	{
		GLRenderer.pushMatrix(matrix);
        Matrix.translateM(matrix, 0, pos.x(), pos.y(), 0);
        Matrix.scaleM(matrix, 0, matrix, 0, dim.x(), dim.y(), 1);
        GLES20.glUseProgram(program);
    	
        simTime += 1f;
        
        int mPositionHandle;
        int mColorHandle;
        int mSizeHandle;
        int mVelocityHandle;
    	int mMVPMatrixHandle;
    	int mSimTimeHandle;
        
    	if((mPositionHandle = cache.get(0)) < 0)
        	mPositionHandle = cache.set(0, GLES20.glGetAttribLocation(program, "aPosition"));
        
    	if((mColorHandle = cache.get(1)) < 0)
    		mColorHandle = cache.set(1, GLES20.glGetAttribLocation(program, "aColor"));
        
    	if((mVelocityHandle = cache.get(2)) < 0)
    		mVelocityHandle = cache.set(2, GLES20.glGetAttribLocation(program, "aVelocity"));
        
    	if((mSizeHandle = cache.get(3)) < 0)
    		mSizeHandle = cache.set(3, GLES20.glGetAttribLocation(program, "aSize"));
        
    	if((mMVPMatrixHandle = cache.get(4)) < 0) 
        	mMVPMatrixHandle = cache.set(4, GLES20.glGetUniformLocation(program, "uMVPMatrix"));
    	
    	if((mSimTimeHandle = cache.get(5)) < 0) 
    		mSimTimeHandle = cache.set(5, GLES20.glGetUniformLocation(program, "uSimTime"));
        
        GLES20.glEnableVertexAttribArray(mColorHandle);
        GLES20.glVertexAttribPointer(
        		mColorHandle, 4,
                GLES20.GL_FLOAT, false,
                0, vertexColorBuffer);

        GLES20.glEnableVertexAttribArray(mSizeHandle);
        GLES20.glVertexAttribPointer(
        		mSizeHandle, 1,
                GLES20.GL_FLOAT, false,
                0, vertexSizeBuffer);
        
        GLES20.glEnableVertexAttribArray(mVelocityHandle);
        GLES20.glVertexAttribPointer(
        		mVelocityHandle, 2,
                GLES20.GL_FLOAT, false,
                0, vertexVelocityBuffer);
        
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        GLES20.glVertexAttribPointer(
                mPositionHandle, 3,
                GLES20.GL_FLOAT, false,
                0, vertexCoordBuffer);
        
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, matrix, 0);
        GLES20.glUniform1f(mSimTimeHandle, simTime);

        GLES20.glDrawArrays(GLES20.GL_POINTS, 0, numberOfParticles);
        
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mColorHandle);
        GLES20.glDisableVertexAttribArray(mSizeHandle);
        GLES20.glDisableVertexAttribArray(mVelocityHandle);
        
        GLRenderer.popMatrix(matrix);
        
	}
    
    
	public GLVec2 getPos() 
	{
		return pos;
	}
    
	public void setPos(GLVec2 pos) 
	{
		this.pos = pos;
	}
    
};
